package com.tiny.tinyweb.core.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelUtils {
    /**
     * 将Excel中的数据转换成{@code Map<String, ArrayList<ArrayList<String>>>}，转换关系如下：<BR/>
     * Sheets-->HashMap<ArrayList<ArrayList<String>>>；Rows-->ArrayList<ArrayList<String>>；Cells-->ArrayList<String>
     * @param file Excel文件
     * @return 转换后的数据
     * @throws FileNotFoundException
     * @throws IOException
     * @throws InvalidFormatException
     */
    public static Map<String, ArrayList<ArrayList<String>>> readExcel(File file)
            throws FileNotFoundException, IOException, InvalidFormatException {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            Workbook wb = WorkbookFactory.create(fis);
            FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
            DataFormatter df = new DataFormatter();

            Sheet sh = null;
            Row row = null;
            int mrw = 0;
            int lrn = 0;
            int lcn = 0;
            int nos = wb.getNumberOfSheets();
            // 所有Sheet的数据保存在HashMap中，Key为Sheet名
            Map<String, ArrayList<ArrayList<String>>> datas = new HashMap<String, ArrayList<ArrayList<String>>>();
            for (int i = 0; i < nos; i++) {
                sh = wb.getSheetAt(i);
                // Sheet中的所有数据行保存在List中，每行数据都是一个ArrayList
                ArrayList<ArrayList<String>> lines = new ArrayList<ArrayList<String>>();
                if (sh.getPhysicalNumberOfRows() > 0) {
                    lrn = sh.getLastRowNum();
                    for (int j = 0; j <= lrn; j++) {
                        row = sh.getRow(j);
                        // 每一行的数据都保存在一个ArrayList中，空白行对应一个大小为零的ArrayList
                        ArrayList<String> line = new ArrayList<String>();
                        lcn = rowToList(row, line, mrw, fe, df);
                        lines.add(line);
                        // 决定最长数据行的长度
                        if (lcn != 0) {
                            mrw = lcn;
                        }
                    }
                }
                datas.put(sh.getSheetName(), lines);
            }
            return datas;
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
    }

    /**
     * 将Excel文件的一行数据转换成一个{@link List}，并返回最长数据行的长度（列数）。
     * @param row Excel文件的行对象
     * @param line 保存转换后数据的列表
     * @param mrw 当前最长数据行的长度
     * @param fe 执行公式计算的{@link org.apache.poi.ss.usermodel.FormulaEvaluator}对象
     * @param df 数据格式话的{@link org.apache.poi.ss.usermodel.DataFormatter}对象
     * @return 当前最长数据行的长度，如果当前行是最长的数据行，则返回当前行的长度
     */
    public static int rowToList(Row row, ArrayList<String> line, int mrw,
            FormulaEvaluator fe, DataFormatter df) {
        // 如果保存转换后数据的列表没有初始化，则先初始化一个空列表
        if (line == null) {
            line = new ArrayList<String>();
        }
        Cell cell = null;
        int lcn = 0;
        // 数据行不存在时直接返回当前行的数据长度为零
        if (row != null) {
            lcn = row.getLastCellNum();
            for (int i = 0; i <= lcn; i++) {
                cell = row.getCell(i);
                if (cell == null) {
                    // 没有值
                    line.add("");
                } else {
                    if (cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
                        // 单元格内容为普通值，获取格式化后的值
                        line.add(df.formatCellValue(cell));
                    } else {
                        // 单元格内容为公式，获取公式执行后的值
                        line.add(df.formatCellValue(cell, fe));
                    }
                }
            }
            // 决定当前最长数据行的长度，如果当前行最长，则返回当前行的长度
            if (lcn > mrw) {
                return lcn;
            } else {
                return mrw;
            }
        } else {
            return 0;
        }
    }

    /**
     * 获取Excle文件里面最大列数
     * @param objExcle Excle文件数据
     * @return 最大列数
     */
    public static int maxCellNum(Map<String, ArrayList<ArrayList<String>>> objExcle) {
        Integer num = null;
        ArrayList<ArrayList<String>> pExclesheetdata = null;
        ArrayList<Integer> cellNumList = new ArrayList<Integer>();
        Set<String> keyset = objExcle.keySet();
        for (String tmpkey : keyset) {
            pExclesheetdata = new ArrayList<ArrayList<String>>();
            pExclesheetdata = objExcle.get(tmpkey);
            for (int i = 0; i < pExclesheetdata.size(); i++) {
                cellNumList.add(pExclesheetdata.get(i).size());
            }
        }
        num = ValidaterUtils.maxIndex(cellNumList);
        return num;
    }

    /**
     * 往Excle里面追加数据
     * @param filepath 文件路径
     * @param objList 追加内容数据
     * @param cellNum 写入的单元格
     * @throws IOException
     */
    public static void excleWrite(String filepath, List<Map<String, Object>> objList, Integer cellNum) throws IOException {
        // Read the spreadsheet that needs to be updated
        FileInputStream fis = new FileInputStream(new File(filepath));
        Workbook wb = null;
        if (filepath.endsWith(".xls")) {
            wb = new HSSFWorkbook(fis);
        } else {
            wb = new XSSFWorkbook(fis);
        }
        // Access the worksheet, so that we can update / modify it.
        Sheet sheet = wb.getSheetAt(0);
        // declare a Cell object
        Cell cell = null;
        Row row = null;
        Map<String, Object> mapExcle = null;
        for (int i = 0; i < objList.size(); i++) {
            mapExcle = new HashMap<String, Object>();
            mapExcle = objList.get(i);
            if (!"".equals(StringUtils.toString(mapExcle.get("StateFlg")))) {
                // Access the second cell in second row to update the value
                row = sheet.getRow(Integer.valueOf(StringUtils.toString(mapExcle.get("DataID"))));
                cell = row.getCell(cellNum);
                if (cell == null) {
                    cell = row.createCell(cellNum);
                }
                // Get current cell value value and overwrite the value
                cell.setCellValue(StringUtils.toString(mapExcle.get("errMsg")));
            }
        }
        // Close the InputStream
        fis.close();
        // Open FileOutputStream to write updates
        FileOutputStream output_file = new FileOutputStream(new File(filepath));
        // write changes
        wb.write(output_file);
        // close the stream
        output_file.close();
        wb.close();
    }

    /**
     * 新规做成Excle
     * @param rows 要写入的行数据
     * @param excleRow 写入开始行
     * @param excleRowCount 要写入的总行数
     * @param fileName 文件名
     * @param sheetName sheet名
     * @return Excle文件
     * @throws IOException
     */
    @SuppressWarnings("resource")
    public static File exportExcel(ArrayList<Map<Integer, Object>> rows, Integer excleRow, Integer excleRowCount, String fileName, String sheetName) throws IOException {
        // 创建excel文件对象
        Workbook wb = null;
        if (fileName.endsWith(".xls")) {
            wb = new HSSFWorkbook();
        } else {
            wb = new XSSFWorkbook();
        }
        // 创建一个张表
        Sheet sheet = wb.createSheet(sheetName);
        // 创建行
        Row row = null;
        Cell cell = null;
        Map<Integer, Object> excleMap = null;
        Set<Integer> keyset = null;
        for (int i = 0; i < excleRowCount + excleRow - 1; i++) {
            row = sheet.createRow(i);
            if (i - excleRow + 1 > rows.size() - 2) {
                break;
            }
            if (i >= excleRow - 2) {
                excleMap = rows.get(i - excleRow + 2);
                keyset = excleMap.keySet();
                for (Integer itgIndex : keyset) {
                    cell = row.createCell(itgIndex);
                    cell.setCellValue(StringUtils.toString(excleMap.get(itgIndex)));
                }
            }
        }
        //输出成XLS文件
        File file =new File(fileName);
        FileOutputStream fos = new FileOutputStream(file);
        //写入数据，并关闭文件
        wb.write(fos);
        fos.close();
        return file;
    }

    /**
     * 将文件转换成字节数组
     * @param pFile 文件
     * @return 字节数组
     * @throws IOException
     */
    public static byte[] convertByteDate(File pFile) throws IOException {
        byte[] b = new byte[1];
        FileInputStream inputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            inputStream = new FileInputStream(pFile);
            byteArrayOutputStream = new ByteArrayOutputStream();
            while (inputStream.read(b) > 0) {
                byteArrayOutputStream.write(b);
            }
            b = byteArrayOutputStream.toByteArray();
        } catch (Exception ex) {
            throw new IOException(ex.getMessage());
        } finally{
            try {
                if(inputStream != null){
                    inputStream.close();
                }
                if(byteArrayOutputStream != null){
                    byteArrayOutputStream.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return b;
    }

    /**
     * 判断是否为数字
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        if (str == null || "".equals(str.trim()) || str.length() > 10)
            return false;
        Pattern pattern = Pattern.compile("^0|[1-9]\\d*(\\.\\d+)?$");
        return pattern.matcher(str).matches();
    }

}
